package com.validant.enigma3.controls;

import java.util.List;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.safari.SafariDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Wait;
import org.openqa.selenium.support.ui.WebDriverWait;

import org.testng.Assert;

import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.validant.enigma3.common.Configuration;
import com.validant.enigma3.common.Constants;
import com.validant.enigma3.common.Utils;
import com.validant.enigma3.common.Constants.*;

public class ExtWebDriver {
	
	private WebDriver driver;
	private String url;
	private long defTimeOut = 0;
	
	public ExtWebDriver() {
		url = Configuration.getBaseURL();
		defTimeOut = Long.parseLong(Configuration.getDefTimeout());
		getDriverInstance();
	}
	
	public ExtWebDriver(String localURL) {
		url = Configuration.getBaseURL() + localURL;
		defTimeOut = Long.parseLong(Configuration.getDefTimeout());
		getDriverInstance();
	}
	
	private void getDriverInstance() {
		switch(Configuration.getBrowser()) {
		case Firefox:
			driver = new FirefoxDriver(); 
			break;
		case Chrome:
			driver = new ChromeDriver();
			break;
		case InternetExplorer:
			driver = new InternetExplorerDriver(); 
			break;
		case Safari:
			driver = new SafariDriver();
			break;
		default:
			driver = new FirefoxDriver();
		}
	}
		
	public WebDriver getWebDriver() {
		return driver;
	}
	
	public void launch() {
		driver.get(url);
	}

	/*
	 * wrap up WebDriver's function
	 */
	public void close() {
		driver.close();
	}
	
	/*
	 * wrap up WebDriver's function
	 */
	public void quit() {
		driver.quit();
	}
	
	/*
	 * wrap up WebDriver's function
	 */
	public WebDriver.Options manage() {
		return driver.manage();		
	}
	
	public String getURL() {
		return url;
	}
	
	/*
	 * wrap up WebDriver's function
	 */
	public String getTitle() {
		return driver.getTitle();
	}
	
	public long getDefTimeOut() {
		return defTimeOut;
	}
		
	/*
	 * wrap up WebDriver's function
	 */
	public WebElement findElement(By locator) {
		WebElement retElement = null; 
		
		try {
			retElement = driver.findElement(locator);
		} catch (ElementNotFoundException e) {
			Assert.assertTrue(false, Constants.ERR_MSG_ELEMENT_NOT_FOUND);
		}
		
		return retElement;
	}
	
	/*
	 * wrap up WebDriver's function
	 */
	public WebElement findElement(Selector selector, String path) {
		return findElement(Utils.getLocator(selector, path));
	}
	
	/*
	 * wrap up WebDriver's function
	 */
	public List<WebElement> findElements(By locator) {
		List<WebElement> webElements = null;
		
		try {
			webElements = driver.findElements(locator);
		} catch (ElementNotFoundException e) {
			Assert.assertTrue(false, Constants.ERR_MSG_ELEMENT_NOT_FOUND);
		}

		return webElements;
	}
	
	/*
	 * wrap up WebDriver's function
	 */
	public List<WebElement> findElements(Selector selector, String path) {
		return findElements(Utils.getLocator(selector, path));
	}
	
	/*
	 * find and return ExtWebElement
	 */
	public ExtWebElement findExtWebElement(By locator) {
		ExtWebElement retElement = null; 
		
		try {
			retElement = new ExtWebElement(driver.findElement(locator));
		} catch (ElementNotFoundException e) {
			Assert.assertTrue(false, Constants.ERR_MSG_ELEMENT_NOT_FOUND);
		}
		
		return retElement;
	}
	
	/*
	 * find and return ExtWebElement
	 */
	public ExtWebElement findExtWebElement(Selector selector, String path) {
		return findExtWebElement(Utils.getLocator(selector, path));
	}
	
	/*
	 * wrap up WebDriver's function
	 */
	public List<ExtWebElement> findExtWebElements(By locator) {
		List<WebElement> webElements = null;
		
		try {
			webElements = driver.findElements(locator);
		} catch (ElementNotFoundException e) {
			Assert.assertTrue(false, Constants.ERR_MSG_ELEMENT_NOT_FOUND);
		}

		return Utils.toExtWebElements(webElements);
	}
	
	/*
	 * wrap up WebDriver's function
	 */
	public List<ExtWebElement> findExtWebElements(Selector selector, String path) {
		return findExtWebElements(Utils.getLocator(selector, path));
	}

	/*
	 * double click on ext web element
	 */
	public void doubleClick(WebElement element) {
		Actions action = new Actions(driver);
		action.doubleClick(element);
		action.perform();
	}
	
	/*
	 * double click on ext web element
	 */
	public void doubleClick(ExtWebElement element) {
		doubleClick(element.getWebElement());
	}
	
	/*
	 * Refresh the page by pressing keys Ctrl+F5
	 * Then wait for the page loaded
	 */
	public void refreshPage() {
		((JavascriptExecutor)driver).executeScript("location.reload()");
		waitForPageLoaded();
	}
	
	/*
	 * Maximize the browser's window
	 */
	public void maximizeWindow() {
		driver.manage().window().maximize();
	}
	
	/*
	 * Wait for page loaded until it timeouts
	 */
	public void waitForPageLoaded() {
		 ExpectedCondition<Boolean> expectation = new
	    		ExpectedCondition<Boolean>() {
	    	 	public Boolean apply(WebDriver driver) {
	    	 		return ((JavascriptExecutor)driver).executeScript("return document.readyState").equals("complete");
	    	 	}
	     };
	     
	     Wait<WebDriver> wait = new WebDriverWait(driver, defTimeOut);
	     try {
	    	 wait.until(expectation);
	     } catch(Throwable error) {
	    	 Assert.assertTrue(false, Constants.ERR_MSG_PAGE_TIME_OUT);	              
	     }
	 }
	
	/*
	 * Wait for loading a control
	 */
	public WebElement waitForElementPresent(By locator) {
		WebDriverWait wait = new WebDriverWait(driver, defTimeOut);
		return wait.until(ExpectedConditions.presenceOfElementLocated(locator));
	}
	
	/*
	 * Wait for loading a control
	 */
	public WebElement waitForElementPresent(Selector selector, String element) {
		return waitForElementPresent(Utils.getLocator(selector, element));
	}
		
	/*
	 * Wait for a control is loaded and displayed visibly
	 */
	public WebElement waitForElementVisible(By locator) {
		WebDriverWait wait = new WebDriverWait(driver, defTimeOut);
		return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
	}
	
	/*
	 * Wait for a control is loaded and displayed visibly
	 */
	public WebElement waitForElementVisible(Selector selector, String element) {
		return waitForElementVisible(Utils.getLocator(selector, element));
	}

	/*
	 * verify whether control is not present on the page
	 */
	public void verifyElementNotPresent(By locator) {
		try {
			driver.findElement(locator);
			Assert.assertTrue(false);
		} catch (org.openqa.selenium.NoSuchElementException e) {
			Assert.assertTrue(true);
		}
	}
	
}
